/**
 * ======================================================
 * stats.cpp 
 *
 * Stat tracking
 * ======================================================
 *
 * The MIT License
 * 
 * Copyright (c) 2008 Matthew Maravillas
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * Version: $Id$
 */

#include <exception>
#include "stats.h"
#include <server_class.h>
#include "util.h"


/*
// Commented 10/28
// Can't figure out how to get this to work; cutting it out for now.

//VFUNC_CALL1(VFUNC_GET_WEAPON_SLOT, CBaseCombatWeapon *, CBaseCombatCharacter, CBaseCombatCharacter_Weapon_GetSlot, int);

// Temporary expansion of the above macro, used while testing
class EmptyClass;
CBaseCombatWeapon * CBaseCombatCharacter_Weapon_GetSlot(CBaseCombatCharacter *pThisPtr, int p1) 
    { 
    DEBUG0("casting this pointer");
    void **this_ptr = *(void ***)&pThisPtr; 
    DEBUG0("getting vtable");
    void **vtable = *(void ***)pThisPtr; 
    DEBUG0("getting function");
    void *func = vtable[VFUNC_GET_WEAPON_SLOT];
    DEBUG0("making union");
    union 
    { 
        CBaseCombatWeapon * (EmptyClass::*mfpnew)(int); 
        struct 
        {
            void *addr; 
            intptr_t adjustor;
        } s; 
    } u; 
    DEBUG0("assigning func ");
    u.s.addr = func; 
    DEBUG0("assigning adjustor");
    u.s.adjustor = sizeof(CBaseCombatCharacter*); 
    DEBUG0("calling function");
    return (CBaseCombatWeapon *) (reinterpret_cast<EmptyClass*>(this_ptr)->*u.mfpnew)(p1);
} */

void Stats::AddPlayer(edict_t *edict, int entityId, int userId, const char *steamId, const char *playerName)
{
    DEBUG0("Stats::AddPlayer");

    int index = EntityIdToIndex(entityId); 

    if ((int)mPlayers.size() <= index)
    {
        DEBUG1("Expanding player vector to %d elements", index + 1);

        mPlayers.resize(index + 1);
    }

    Player *player = GetPlayer(entityId);
 
    if (player != NULL)
    {
        DEBUG1("Initializing player for entity id %d", entityId);

        player->Initialize(edict, entityId, userId, steamId, playerName);

#ifdef _DEBUG
        LogEntityMessage(entityId, "added to stats");
#endif
    }
    else
    {
        META_LOG(g_PLAPI, "Couldn't expand player vector for a new player");
    }
}

void Stats::RemovePlayer(int entityId)
{       
    DEBUG0("Stats::RemovePlayer");

    Player *player = GetPlayer(entityId);

    if (player != NULL)
    {
#ifdef _DEBUG
        LogEntityMessage(entityId, "being removed from stats");
#endif
        player->Disconnect();
    }
    else
    {
        META_LOG(g_PLAPI, "Couldn't remove player from stats");
    }
}

void Stats::UpdateHealing(int userId, const char *eventName)
{
    DEBUG1("Stats::UpdateHealing for userId: %d", userId);
    
    Player *player = GetPlayerByUserId(userId);  

    if (player != NULL) 
    {
        int healing = *(int*)mSendProps->GetValue(
            player->GetEdict()->GetUnknown()->GetBaseEntity(), 
            mSendProps->HEAL_POINTS_OFFSET);

        int recentHealing = player->UpdateHealing(healing);

        if (recentHealing > 0)
        {
            META_LOG(g_PLAPI, "%s healed %d points (%s)", player->ToString().c_str(), recentHealing, eventName);
        }
    }
}
/*
// Commented 10/28
// Still can't figure out how to get the right function pointer for Weapon_GetSlot

void Stats::UpdateChargeLevel(int userId, const char *eventName)
{
    Player *player = GetPlayerByUserId(userId);  

    DEBUG0("Getting base entity");

    CBaseEntity *baseEntity = player->GetEdict()->GetUnknown()->GetBaseEntity();

    DEBUG0("Initializing virtual func");

//   CBasePlayer_Weapon_GetSlot.Get(baseEntity, CBasePlayer_Weapon_GetSlot_Offset);

    DEBUG0("Calling virtual func");

    if (baseEntity->MyCombatCharacterPointer() == NULL) 
    {
        DEBUG0("Combat character pointer was null");
    }
    else 
    {
        CBaseCombatWeapon* weapon = CBaseCombatCharacter_Weapon_GetSlot(baseEntity->MyCombatCharacterPointer(), 1);

        DEBUG1("weapon slot 1: %s", weapon->GetName());
    }

}*/

void Stats::LogUserMessage(int userId, const char *message)
{
    DEBUG0("Stats::LogUserMessage");

    Player *player = GetPlayerByUserId(userId);

    if (player != NULL)
    {
        LogPlayerMessage(player, message);
    }
}

inline void Stats::LogEntityMessage(int entityId, const char *message)
{
    DEBUG0("Stats::LogEntityMessage");

    Player *player = GetPlayer(entityId);

    if (player != NULL)
    {
        LogPlayerMessage(player, message);
    }
}

std::string Stats::GetPlayerString(int userId)
{
    Player *player = GetPlayerByUserId(userId);

    return player->ToString();
}

inline void Stats::LogPlayerMessage(Player *player, const char *message)
{
    DEBUG0("Stats::LogPlayerMessage");
        
    META_LOG(g_PLAPI, "%s %s", player->ToString().c_str(), message);
}

Player *Stats::GetPlayer(int entityId) 
{
    DEBUG0("Stats::GetPlayer");

    int index = EntityIdToIndex(entityId);

    if (index < 0 ||
        index >= (int)mPlayers.size()) {
        META_LOG(g_PLAPI, "Player not found for entity id %d", entityId);
        return NULL;
    }

    return &(mPlayers[index]); 
}

Player *Stats::GetPlayerByUserId(int userId)
{
    DEBUG1("Looking for player with user id %d", userId);

    for (std::vector<Player>::iterator itr = mPlayers.begin(); itr != mPlayers.end(); ++itr) 
    {
        if ((*itr).GetUserId() == userId)
        {
            return &(*itr);
        }
    }

    return NULL;
}